﻿Namespace LDR.Improve

    ''' <summary>Scale3x is real-time graphics effect able to increase the size of small bitmaps guessing the missing pixels without interpolating pixels and blurring the images.</summary>
    ''' <remarks></remarks>
    Public Class Scale3X
        Implements IFilter

        Private _InputImg As Drawing.Image
        Private _OutputImg As Drawing.Image
        Private _InputBmp As TheiaLib.Imaging.GDIBitmap
        Private _OutputBmp As TheiaLib.Imaging.GDIBitmap

        Private _MaxDelta As Integer = 8

        Public Sub New()

        End Sub

        Public Sub New(ByVal InputBmp As Drawing.Image)
            _InputImg = InputBmp
        End Sub

        Public ReadOnly Property Applied() As Boolean Implements IFilter.Applied
            Get
                Return _OutputImg IsNot Nothing
            End Get
        End Property

        Public Sub Apply() Implements IFilter.Apply
            If _InputImg Is Nothing Then Throw New NullReferenceException("No input image defined.")

            _OutputImg = New Drawing.Bitmap(_InputImg.Width, _InputImg.Height, _InputImg.PixelFormat)
            _OutputBmp = New TheiaLib.Imaging.GDIBitmap(_OutputImg)
            _InputBmp = New TheiaLib.Imaging.GDIBitmap(_InputImg)

            'Apply(New Drawing.Rectangle(0, 0, _InputImg.Width, _InputImg.Height))
            Dim ApplyThread1 As New Thread(AddressOf ApplyQuadrant1)
            Dim ApplyThread2 As New Thread(AddressOf ApplyQuadrant2)
            Dim ApplyThread3 As New Thread(AddressOf ApplyQuadrant3)
            Dim ApplyThread4 As New Thread(AddressOf ApplyQuadrant4)

            ApplyThread1.Start()
            ApplyThread2.Start()
            ApplyThread3.Start()
            ApplyThread4.Start()

            Do While ApplyThread1.ThreadState = Threading.ThreadState.Running OrElse _
                     ApplyThread2.ThreadState = Threading.ThreadState.Running OrElse _
                     ApplyThread3.ThreadState = Threading.ThreadState.Running OrElse _
                     ApplyThread4.ThreadState = Threading.ThreadState.Running

            Loop

            _InputBmp.UnLockBitmap()
            _OutputBmp.UnLockBitmap()
        End Sub

        Private Sub ApplyQuadrant1()
            Dim Left As Integer = 0
            Dim Top As Integer = 0
            Dim Width As Integer = Math.Round(_InputImg.Width / 2, 0, MidpointRounding.ToEven)
            Dim Height As Integer = Math.Round(_InputImg.Height / 2, 0, MidpointRounding.ToEven)
            Apply(New Drawing.Rectangle(Left, Top, Width, Height))
        End Sub

        Private Sub ApplyQuadrant2()
            Dim Left As Integer = Math.Round(_InputImg.Width / 2, 0, MidpointRounding.ToEven)
            Dim Top As Integer = 0
            Dim Width As Integer = _InputImg.Width - Left
            Dim Height As Integer = Math.Round(_InputImg.Height / 2, 0, MidpointRounding.ToEven)
            Apply(New Drawing.Rectangle(Left, Top, Width, Height))
        End Sub

        Private Sub ApplyQuadrant3()
            Dim Left As Integer = 0
            Dim Top As Integer = Math.Round(_InputImg.Height / 2, 0, MidpointRounding.ToEven)
            Dim Width As Integer = Math.Round(_InputImg.Width / 2, 0, MidpointRounding.ToEven)
            Dim Height As Integer = _InputImg.Height - Top
            Apply(New Drawing.Rectangle(Left, Top, Width, Height))
        End Sub

        Private Sub ApplyQuadrant4()
            Dim Left As Integer = Math.Round(_InputImg.Width / 2, 0, MidpointRounding.ToEven)
            Dim Top As Integer = Math.Round(_InputImg.Height / 2, 0, MidpointRounding.ToEven)
            Dim Width As Integer = _InputImg.Width - Left
            Dim Height As Integer = _InputImg.Height - Top
            Apply(New Drawing.Rectangle(Left, Top, Width, Height))
        End Sub

        Private Function ColorDist(ByVal Col1 As Drawing.Color, ByVal Col2 As Drawing.Color) As Integer
            Return Math.Sqrt(Math.Pow(CInt(Col1.R) - CInt(Col2.R), 2) + _
                             Math.Pow(CInt(Col1.G) - CInt(Col2.G), 2) + _
                             Math.Pow(CInt(Col1.B) - CInt(Col2.B), 2))
        End Function

        Private Function IsEqualColor(ByVal Col1 As Drawing.Color, ByVal Col2 As Drawing.Color) As Integer
            Return ColorDist(Col1, Col2) <= _MaxDelta
        End Function


        Private Function IsNotEqualColor(ByVal Col1 As Drawing.Color, ByVal Col2 As Drawing.Color) As Integer
            Return Not IsEqualColor(Col1, Col2)
        End Function

        Private Sub Apply(ByVal ClipBounds As System.Drawing.Rectangle)

            Dim tmpCol As Drawing.Color
            Dim PM(0 To 2, 0 To 2) As Drawing.Color
            Dim E(0 To 9) As Drawing.Color


            For y As Integer = ClipBounds.Y To ClipBounds.Y + ClipBounds.Height - 1
                For x As Integer = ClipBounds.X To ClipBounds.X + ClipBounds.Width - 1

                    For i As Integer = -1 To 1
                        For j As Integer = -1 To 1
                            PM(i + 1, j + 1) = _InputBmp.Pixel2(x + i, y + j)
                        Next
                    Next

                    If IsNotEqualColor(PM(0, 1), PM(2, 1)) AndAlso _
                       IsNotEqualColor(PM(1, 0), PM(1, 2)) Then
                        'E0
                        If IsEqualColor(PM(1, 0), PM(0, 1)) Then : E(0) = PM(0, 1) : Else : E(0) = PM(1, 1) : End If
                        'E1
                        If (IsEqualColor(PM(1, 0), PM(0, 1)) AndAlso IsNotEqualColor(PM(1, 1), PM(2, 0))) OrElse _
                           (IsEqualColor(PM(1, 0), PM(2, 1)) AndAlso IsNotEqualColor(PM(1, 1), PM(0, 0))) Then
                            E(1) = PM(1, 0) : Else : E(1) = PM(1, 1)
                        End If
                        'E2
                        If IsEqualColor(PM(1, 0), PM(2, 1)) Then : E(2) = PM(2, 1) : Else : E(2) = PM(1, 1) : End If
                        'E3
                        If (IsEqualColor(PM(1, 0), PM(0, 1)) AndAlso IsNotEqualColor(PM(1, 1), PM(0, 2))) OrElse _
                           (IsEqualColor(PM(0, 1), PM(1, 2)) AndAlso IsNotEqualColor(PM(1, 1), PM(0, 0))) Then
                            E(3) = PM(0, 1) : Else : E(3) = PM(1, 1)
                        End If
                        'E4
                        E(4) = PM(1, 1)
                        'E5
                        If (IsEqualColor(PM(1, 0), PM(2, 1)) AndAlso IsNotEqualColor(PM(1, 1), PM(2, 2))) OrElse _
                           (IsEqualColor(PM(2, 1), PM(1, 2)) AndAlso IsNotEqualColor(PM(1, 1), PM(2, 0))) Then
                            E(3) = PM(2, 1) : Else : E(3) = PM(1, 1)
                        End If
                        'E6
                        If IsEqualColor(PM(0, 1), PM(1, 2)) Then : E(6) = PM(0, 1) : Else : E(6) = PM(1, 1) : End If
                        'E7
                        If (IsEqualColor(PM(1, 0), PM(2, 1)) AndAlso IsNotEqualColor(PM(1, 1), PM(2, 2))) OrElse _
                           (IsEqualColor(PM(2, 1), PM(1, 2)) AndAlso IsNotEqualColor(PM(1, 1), PM(0, 2))) Then
                            E(3) = PM(2, 1) : Else : E(3) = PM(1, 1)
                        End If
                        'E8
                        If IsEqualColor(PM(2, 1), PM(1, 2)) Then : E(8) = PM(2, 1) : Else : E(8) = PM(1, 1) : End If
                    Else
                        For i As Integer = 0 To 8 : E(i) = PM(1, 1) : Next
                    End If


                    Dim SumR As Double = 0
                    Dim SumG As Double = 0
                    Dim SumB As Double = 0
                    For i = 0 To 8 : SumR += E(i).R : SumG += E(i).G : SumB += E(i).B : Next
                    _OutputBmp.Pixel(x, y) = System.Drawing.Color.FromArgb(SumR / 9.0, SumG / 9.0, SumB / 9.0)
                Next
            Next



        End Sub

        Public Property InputImage() As Drawing.Image Implements IFilter.InputImage
            Get
                Return _InputImg
            End Get
            Set(ByVal value As Drawing.Image)
                If _InputBmp IsNot Nothing AndAlso _InputBmp.IsLocked Then _InputBmp.UnLockBitmap()
                If _OutputBmp IsNot Nothing AndAlso _OutputBmp.IsLocked Then _OutputBmp.UnLockBitmap()
                _InputBmp = Nothing
                _OutputBmp = Nothing
                _InputImg = value
                _OutputImg = Nothing
            End Set
        End Property

        Public ReadOnly Property OutputImage() As Drawing.Image Implements IFilter.OutputImage
            Get
                Return _OutputImg
            End Get
        End Property

        Public Property MaxDelta() As Integer
            Get
                Return _MaxDelta
            End Get
            Set(ByVal value As Integer)
                _MaxDelta = Math.Min(Math.Max(value, Byte.MinValue), Byte.MaxValue)
            End Set
        End Property
    End Class
End Namespace

